Skip to content

refactor: replace @voidzero-dev/vite-plus-test with upstream vitest@4.1.5#1588

Open
Brooooooklyn wants to merge 36 commits into
mainfrom
refactor/replace-vite-plus-test-with-vitest
Open

refactor: replace @voidzero-dev/vite-plus-test with upstream vitest@4.1.5#1588
Brooooooklyn wants to merge 36 commits into
mainfrom
refactor/replace-vite-plus-test-with-vitest

Conversation

@Brooooooklyn
Copy link
Copy Markdown
Member

@Brooooooklyn Brooooooklyn commented May 15, 2026

Summary

Deletes the bundled @voidzero-dev/vite-plus-test wrapper and consumes upstream vitest@4.1.5 (plus @vitest/browser*) directly. The vite redirection role that drove the wrapper is now handled cleanly by package-manager overrides (vite@voidzero-dev/vite-plus-core), so the bundle was dead weight that lagged upstream releases.

Public API contract preserved:

  • vite-plus/test* IS the public test API — existing user code (import { vi } from 'vite-plus/test', etc.) is NEVER rewritten.
  • New vite-plus users don't install vitest or @vitest/* separately; they come in transitively as direct deps of vite-plus.
  • vp migrate on an upstream-vitest project still forward-migrates vitest, vitest/*, @vitest/browser*, declare-module specifiers, and /// <reference types> directives to the vite-plus/test* surface (one-time transition).

Notable changes:

  • packages/cli/build.ts: syncTestPackageExports auto-generates ./test/* shims from upstream vitest's exports map, plus ./test/<provider> and ./test/browser/providers/<short> shims projected from each @vitest/browser-* package's exports.
  • packages/cli/package.json: adds @vitest/browser, @vitest/browser-playwright, @vitest/browser-preview, @vitest/browser-webdriverio as direct catalog deps pinned to 4.1.5.
  • crates/vite_global_cli/src/commands/version.rs: vitest ToolSpec points at the vitest package directly.
  • packages/cli/src/resolve-test.ts: resolves vitest/package.json and reads bin.vitest so vp test invokes upstream vitest.
  • packages/cli/src/utils/constants.ts: drops vitest from VITE_PLUS_OVERRIDE_PACKAGES; only vite remains a managed key.
  • packages/cli/src/migration/migrator.ts:
    • Adds an isVitestAdjacent flag that flips needVitePlus = true for projects with packages like vitest-browser-svelte even when there's no vite/oxlint/tsdown to migrate.
    • Adds pruneLegacyWrapperAliases / pruneYamlMapLegacyWrapperAliases sweeps that rewrite stale vitest: npm:@voidzero-dev/vite-plus-test@* aliases to ^4.1.5 (so existing catalog: refs keep resolving) and drop other stale wrapper-targeted keys.
  • packages/cli/src/migration/bin.ts: adds a handleInstallResult helper so failed reinstalls warn the user, append to report.warnings, and flip process.exitCode instead of being silently reported as success.

User-visible behavior changes

vp test -h and live test runs now show vitest's native banner (vitest/<semver>, RUN v<semver> <cwd>) instead of the wrapper-rebranded output (vp test/<semver>, RUN <cwd>). This is the tradeoff for delegating directly to upstream vitest without a wrapper layer.

Test plan

  • cargo test -p vite_migration --lib: 167 tests pass
  • pnpm exec vitest run (packages/cli): 374 tests pass
  • pnpm bootstrap-cli succeeds
  • pnpm -F vite-plus snap-test-global + snap-test-local: all fixtures regenerated; diffs only reflect expected behavior (forward import rewrites, @vitest/browser* removed from user devDeps, playwright/webdriverio preserved as peers, stale vite-plus-test catalog aliases normalized to ^4.1.5).
  • Manual end-to-end: vp test on a fixture using import { vi } from 'vite-plus/test'; vi.mock(...). See "Follow-up" below.
  • Manual end-to-end: vp migrate on a fresh upstream-vitest project.
  • pnpm install clean: zero @voidzero-dev/vite-plus-test references in the lockfile, browser-provider packages installed transitively via vite-plus.

Follow-up

@vitest/mocker hoists vi.mock(...) calls only when the import source is the literal string 'vitest' (@vitest/mocker@4.1.5/dist/chunk-hoistMocks.js hardcodes hoistedModule = "vitest"). User code that imports vi from 'vite-plus/test' won't get its mocks hoisted, which silently breaks mocking. The plan is to contribute upstream a hoistedModule?: string | string[] option to hoistMocks() so vite-plus can pass ['vitest', 'vite-plus/test']. Tracked as a separate effort — this PR ships the wrapper removal independently; the mocker fix can land later as a @vitest/mocker point release.

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk because it changes how vitest is packaged/resolved (exports, dependency overrides, and CI tgz repacking), which can impact test execution and TypeScript type resolution across multiple package managers (notably bun).

Overview
Switches vite-plus/test* to be generated shims over upstream vitest instead of publishing/consuming @voidzero-dev/vite-plus-test, including projecting @vitest/browser-* providers under both legacy and new alias paths and adding special handling to avoid TypeScript type-identity splits.

Updates dependency/version plumbing: pnpm-workspace.yaml catalog bump logic now targets plain vitest + @vitest/browser*, the upgrade script updates a single VITEST_VERSION constant used by CI tooling, and packages/cli now depends directly on vitest/@vitest/browser* while removing wrapper build/publish steps.

Adjusts migration + tooling behavior: the migration rewriter stops rewriting declare module 'vitest*' augmentations (imports still rewrite), adds .cjs/.cts files to the rewrite walker, and ecosystem/CI workflows now pack only core+cli, repack a masquerade vite-7.99.0.tgz for bun peer-dep strictness, and drop vite-plus-test from releases/docs/publishing.

Reviewed by Cursor Bugbot for commit 1a5b269. Bugbot is set up for automated code reviews on this repo. Configure here.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit 1a5b269
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a083fc957edd90008bcf321

Comment thread packages/cli/src/migration/migrator.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 365a61de42

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/resolve-test.ts
Brooooooklyn added a commit that referenced this pull request May 15, 2026
…mat/typo

PR #1588 CI failures:
- Force-override mode (VP_FORCE_MIGRATE=1, set by test-vp-create.yml and
  ecosystem-ci) now re-pins any pre-existing vite-plus range to the local
  tgz path in monorepo workspace packages. Without this, pnpm reads the
  published vite-plus@0.1.21 metadata to resolve transitive deps including
  @voidzero-dev/vite-plus-test@0.1.21, which shadowed upstream vitest@4.1.5
  at runtime and broke vp create monorepo tests.
- typos CI: rename yarn-PnP to yarn Plug'n'Play (Pn→On false positive).
- vp check: format packages/cli/build.ts shim-generation block.
- Rename __dirname in install-failure-guard.spec.ts to satisfy
  eslint(no-underscore-dangle).
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fafa67971

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/build.ts
Comment thread .github/scripts/upgrade-deps.ts
Comment thread packages/cli/src/migration/bin.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec69abaadb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/package.json
Comment thread packages/cli/src/migration/migrator.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 381b6e2c20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/migration/migrator.ts Outdated
Comment thread packages/cli/build.ts
Brooooooklyn added a commit that referenced this pull request May 15, 2026
…mat/typo

PR #1588 CI failures:
- Force-override mode (VP_FORCE_MIGRATE=1, set by test-vp-create.yml and
  ecosystem-ci) now re-pins any pre-existing vite-plus range to the local
  tgz path in monorepo workspace packages. Without this, pnpm reads the
  published vite-plus@0.1.21 metadata to resolve transitive deps including
  @voidzero-dev/vite-plus-test@0.1.21, which shadowed upstream vitest@4.1.5
  at runtime and broke vp create monorepo tests.
- typos CI: rename yarn-PnP to yarn Plug'n'Play (Pn→On false positive).
- vp check: format packages/cli/build.ts shim-generation block.
- Rename __dirname in install-failure-guard.spec.ts to satisfy
  eslint(no-underscore-dangle).
@Brooooooklyn Brooooooklyn force-pushed the refactor/replace-vite-plus-test-with-vitest branch from 5c48da8 to 39efcbf Compare May 15, 2026 13:15
Comment thread packages/cli/src/migration/bin.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39efcbf239

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/vite_migration/src/import_rewriter.rs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2fceee5296

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/resolve-test.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b888329d76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/migration/migrator.ts
@Brooooooklyn
Copy link
Copy Markdown
Member Author

Upstream blockers (still needed after the fixes in this PR)

  • @vitest/mocker: the static hoister hardcodes hoistedModule = "vitest" so vi.mock() calls authored as import { vi } from 'vite-plus/test' are silently not hoisted — we ship a defineConfig-injected pre-stage Vite plugin (commit d69fe03) that rewrites 'vite-plus/test''vitest' at transform time as a workaround, but the long-term fix is exposing hoistedModule as a configurable option upstream.
  • rolldown/oxc parser: rejects the legal TypeScript syntax import type Default, { Named } from 'mod', breaking vp build against .d.ts files such as postcss@8.5.8's that use this form.

Comment thread .github/workflows/test-vp-create.yml Outdated
Comment thread ecosystem-ci/patch-project.ts Outdated
@Brooooooklyn
Copy link
Copy Markdown
Member Author

Upstream blockers (updated)

After the latest fixes (21937c5b1), the remaining upstream items are:

  • @vitest/mocker: hardcodes hoistedModule = "vitest" in its static hoister, so any redistributor that surfaces vi under a different specifier breaks vi.mock() silently — we now rewrite 'vite-plus/test''vitest' in the Rust migrator (commit 21937c5) plus keep a runtime Vite plugin (d69fe03) as a safety net, but the long-term fix is exposing hoistedModule as a configurable option upstream so vite-plus/test could also be recognized natively.
  • rolldown/oxc parser: rejects the legal TypeScript syntax import type Default, { Named } from 'mod', breaking vp build against .d.ts files such as postcss@8.5.8's that use this form (only vp-config E2E remains failing on this).

@Brooooooklyn
Copy link
Copy Markdown
Member Author

Final status (after b6b5b8b)

The aggressive Rust source-rewrite in 21937c5 (which made 'vitest' canonical) was reverted because it broke vp create templates that use from "vitest" and rely on the forward rewrite to resolve to vite-plus/test. The reverse-direction migration was over-reach and the cure was worse than the disease for the affected projects (vinext's 4 vi.mock files).

Confirmed upstream blockers:

  • @vitest/mocker: hardcodes hoistedModule = "vitest" in its static hoister, so any redistributor surfacing vi under a different specifier breaks vi.mock() silently — affects ~4 files in vinext that import vi from vite-plus/test (the runtime plugin in d69fe03 handles non-mocker vi usage but cannot intercept the static hoister which runs in vitest's own plugin pipeline). The long-term fix is exposing hoistedModule as a configurable option upstream.
  • rolldown/oxc parser: rejects the legal TypeScript syntax import type Default, { Named } from 'mod', breaking vp build against .d.ts files such as postcss@8.5.8's that use this form (only vp-config E2E remains failing on this).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6b5b8b0bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/index.ts
Comment thread .github/scripts/upgrade-deps.ts
Comment thread crates/vite_migration/src/import_rewriter.rs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60b7d0f0ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/index.ts
Comment thread packages/cli/src/utils/constants.ts
@Brooooooklyn
Copy link
Copy Markdown
Member Author

Final status — branch HEAD 3ffb7cd

E2E now at expected steady state. The two remaining upstream items:

  • @vitest/mocker hardcodes hoistedModule = "vitest" so the static mock hoister doesn't recognize vi imported via redistributor specifiers — needs upstream PR exposing hoistedModule as a configurable option. (Workaround in this PR: defineConfig injects a pre-stage Vite plugin that rewrites 'vite-plus/test''vitest', propagated into test.projects entries.)
  • rolldown/oxc parser rejects valid TypeScript import type Default, { Named } from 'mod' syntax — breaks vp build against postcss@8.5.8's .d.ts declarations (only vp-config E2E still fails on this).

Everything else (npmx.dev, vinext, vue-mini, frm-stack, varlet, vitepress, reactive-resume, rollipop, dify, etc.) is now green.

Brooooooklyn and others added 6 commits May 16, 2026 17:18
….1.5

Delete the bundled `@voidzero-dev/vite-plus-test` wrapper and consume
upstream `vitest@4.1.5` (plus `@vitest/browser*`) directly. The vite
redirection that the wrapper used to provide is now handled cleanly
by package-manager overrides (`vite` -> `@voidzero-dev/vite-plus-core`),
so the bundle was dead weight that lagged upstream vitest releases.

Public API contract preserved:
- `vite-plus/test*` IS the public test API. Existing vite-plus user
  code (e.g. `import { vi } from 'vite-plus/test'`) is NEVER rewritten —
  the imports stay exactly as authored.
- New vite-plus users do NOT install `vitest` or `@vitest/*` directly.
  They install `vite-plus`; vitest and the browser providers come in
  transitively as direct deps of the CLI package.
- `vp migrate` on an upstream-vitest project still rewrites bare
  `vitest`, `vitest/*`, `@vitest/browser*`, declare-module specifiers
  and `/// <reference types>` directives to the `vite-plus/test*`
  surface (one-time forward migration).

Implementation:
- packages/cli/build.ts `syncTestPackageExports` auto-generates
  `./test/*` shims from `vitest`'s own exports map, plus
  `./test/<provider>` and `./test/browser/providers/<short>` shims
  projected from each `@vitest/browser-*` package's exports.
- packages/cli/package.json adds `@vitest/browser`,
  `@vitest/browser-playwright`, `@vitest/browser-preview`,
  `@vitest/browser-webdriverio` as direct catalog deps pinned to
  4.1.5 alongside `vitest`.
- crates/vite_global_cli/src/commands/version.rs version ToolSpec
  points at the `vitest` package directly.
- packages/cli/src/resolve-test.ts resolves `vitest/package.json`
  and reads `bin.vitest` so `vp test` invokes upstream vitest.
- packages/cli/src/utils/constants.ts drops `vitest` from
  `VITE_PLUS_OVERRIDE_PACKAGES`; only `vite` remains a managed key.
- packages/cli/src/migration/migrator.ts adds an `isVitestAdjacent`
  flag that flips `needVitePlus = true` for projects with packages
  like `vitest-browser-svelte` even when no vite/oxlint/tsdown dep
  is being migrated; adds a `pruneLegacyWrapperAliases` /
  `pruneYamlMapLegacyWrapperAliases` sweep that rewrites stale
  `vitest: npm:@voidzero-dev/vite-plus-test@*` aliases to `^4.1.5`
  in package.json overrides/resolutions/pnpm.overrides, in
  pnpm-workspace.yaml catalog/catalogs/overrides, and in bun
  workspaces.catalog / workspaces.catalogs.
- packages/cli/src/migration/bin.ts adds a `handleInstallResult`
  helper so failed reinstalls warn the user, append to
  `report.warnings`, and flip `process.exitCode` instead of being
  silently reported as success.

Verification:
- cargo test -p vite_migration --lib: 167 tests pass
- pnpm exec vitest run (packages/cli): 355 tests pass
- pnpm bootstrap-cli + snap-test-global + snap-test-local: all
  fixtures regenerated; expected diffs only (forward import
  rewrites, `@vitest/browser*` removed from user devDeps,
  `playwright`/`webdriverio` preserved as peers, stale
  `vite-plus-test` catalog aliases normalized to `^4.1.5`).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…mat/typo

PR #1588 CI failures:
- Force-override mode (VP_FORCE_MIGRATE=1, set by test-vp-create.yml and
  ecosystem-ci) now re-pins any pre-existing vite-plus range to the local
  tgz path in monorepo workspace packages. Without this, pnpm reads the
  published vite-plus@0.1.21 metadata to resolve transitive deps including
  @voidzero-dev/vite-plus-test@0.1.21, which shadowed upstream vitest@4.1.5
  at runtime and broke vp create monorepo tests.
- typos CI: rename yarn-PnP to yarn Plug'n'Play (Pn→On false positive).
- vp check: format packages/cli/build.ts shim-generation block.
- Rename __dirname in install-failure-guard.spec.ts to satisfy
  eslint(no-underscore-dangle).
…r migration

The migration step intentionally rewrites overrides/catalogs/deps so the
existing lockfile is guaranteed to be stale. pnpm and yarn default to
frozen-lockfile mode under CI, so they refuse the reinstall with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH and the new handleInstallResult enforcement
propagates that failure as a non-zero exit. Add --no-frozen-lockfile alongside
the existing --force for npm/bun, applied to both the post-migration reinstall
and the ESLint/Prettier early-return reinstall.
…r types through vite-plus, prune monorepo aliases

Three independent fixes for E2E failures introduced by the vite-plus-test → upstream-vitest refactor:

1. Stop rewriting `declare module 'vitest'` (and subpaths/@vitest variants).
   The `vite-plus/test*` shims re-export upstream `vitest*`, so the type identity
   is upstream. Augmenting `'vite-plus/test'` only augments the shim and never
   merges into the upstream types the user actually sees through `import { expect }
   from 'vite-plus/test'`. Import rewrites stay; declare-module rewrites are dropped.

2. Inline-copy upstream `@vitest/browser-*` d.ts content into the test shims and
   rewrite `vitest/node`/`vitest/browser`/`@vitest/browser*` bare specifiers to
   relative paths inside the `dist/test/` tree. Without this, pnpm splits
   `@vitest/browser-playwright` and `vitest` across different peer-dep edges,
   creating two `BrowserProvider` type identities — and `provider: playwright()`
   fails the user's vite.config typecheck.

3. Add the same `pruneLegacyWrapperAliases` calls to `rewriteRootWorkspacePackageJson`
   that `rewriteStandaloneProject` already had. Monorepo migrations were leaving
   stale `npm:@voidzero-dev/vite-plus-test@*` aliases in root resolutions /
   overrides / pnpm.overrides, causing pnpm to install the deleted wrapper.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…talls

vitest@4.1.5 declares a `vite^6/^7/^8` peer dep. When the user's project
overrides `vite` to `@voidzero-dev/vite-plus-core` (version 0.0.0 in dev
and 0.1.21 on npm — neither matches), bun aborts with:
  error: vite@^6.0.0 || ^7.0.0 || ^8.0.0 failed to resolve

pnpm/yarn/npm tolerate this redirect; bun does not, and offers no
`peerDependencyRules`-style escape hatch — only `[install] peer = false`
in `bunfig.toml`. vite-plus already provides the vite surface the user
needs, so disabling bun's auto-install of *missing* peers is safe here:
the redirected vite is the only one that ever fails, and other vitest
peers (jsdom, happy-dom, @vitest/*, etc.) are upstream-optional and
either pulled in transitively or user-installed.

`ensureBunfigPeerSuppression` writes/merges `bunfig.toml` whenever the
migrator touches a bun project — both the monorepo path (via
`rewriteBunCatalog`) and the standalone path (in
`rewriteStandaloneProject`'s post-package.json branch). Honors any
pre-existing `peer =` setting so a user who deliberately set `peer = true`
keeps it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… behavior

Earlier drafts of this branch claimed the migrator stopped rewriting `vitest`
runtime imports and that the browser-provider subpaths were no longer
re-exported. That landed in docs/guide/migrate.md, rfcs/migration-command.md,
and packages/cli/BUNDLING.md, but the implementation kept the forward rewrites
(`vitest` → `vite-plus/test`, `@vitest/browser*` → `vite-plus/test/browser*`)
and added the inlined-d.ts shim infrastructure that *does* re-export every
provider subpath under both `./test/<pkg>` and `./test/browser/providers/<short>`.

The only thing actually preserved is `declare module 'vitest'` / `declare
module '@vitest/browser*'` — those have to target the upstream module identity
to merge into the types `vite-plus/test*` re-exports.

This commit restores the migrate guide's "import { vi } from 'vite-plus/test'"
example, restores the full rewrite-rule list in the migration-command RFC,
fixes the bundling doc's "provider subpaths are not re-exported" claim, and
adds a short subsection explaining why the provider d.ts shims are inlined
instead of bare re-exports (the pnpm-edge type-identity split that would
otherwise break user `provider: playwright()` typechecks).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brooooooklyn and others added 19 commits May 16, 2026 17:18
Mirroring the override target as a devDep wasn't enough — bun also
validates that the resolved package's package.json#name matches the
requested specifier. The file: target was @voidzero-dev/vite-plus-core,
not vite, so bun still aborts with "vite@^6.0.0 || ^7.0.0 || ^8.0.0
failed to resolve" even after the devDep injection landed in 61289fb.

Pack a second tgz with name=vite, version=7.99.0 (same code as core)
and point VP_OVERRIDE_PACKAGES.vite at it. The user's package.json now
gets:
  overrides.vite        = file:.../vite-7.99.0.tgz   (name=vite)
  overrides.@voidzero…  = file:.../core-7.99.0.tgz   (name=core)
  devDependencies.vite  = file:.../vite-7.99.0.tgz   (name=vite)
which satisfies both of bun's checks; pnpm/npm/yarn don't care.

Three pack steps now run sequentially:
1. set name=vite + version=7.99.0, pack core → vite-7.99.0.tgz
2. restore name=@voidzero-dev/vite-plus-core, pack core →
   voidzero-dev-vite-plus-core-7.99.0.tgz
3. pack cli (workspace:* → core@7.99.0 so install-global-cli matches)
4. git checkout the source package.json
…ng during pack

The previous masquerade approach renamed packages/core/package.json
between two pnpm pack calls. Even with the name restored before packing
the CLI, pnpm's workspace state was polluted: catalog `vite: workspace:@voidzero-dev/vite-plus-core@*` resolved to literal `vite@7.99.0` in
the packed CLI manifest, and pnpm then failed at install time with
`ERR_PNPM_NO_MATCHING_VERSION` looking up vite@7.99.0 in the npm
registry.

Pack core (version bumped to 7.99.0, name unchanged) and the CLI
normally, then run a new `tool repack-vite-tgz` subcommand that reads
the core tgz, rewrites the inner `package.json#name` to "vite", and
writes a sibling `vite-7.99.0.tgz`. The repacked tgz still satisfies
bun's strict name+version peer check for vitest 4.1.5's `peer vite ^6 || ^7 || ^8`, but no workspace pollution occurs because we never modify the in-tree package name.
Pointing every package manager at the renamed vite-7.99.0.tgz worked
around bun's strict peer check but broke pnpm: when both a workspace
sub-package and the workspace override resolve to the same tgz that
advertises itself as vite@7.99.0, pnpm tries to validate the version
against the registry and aborts with ERR_PNPM_NO_MATCHING_VERSION.

Revert to the main-branch shape for pnpm/npm/yarn (same core tgz for
both `vite` and `@voidzero-dev/vite-plus-core` overrides) and keep the
renamed tgz only for bun, where vitest's `peer vite ^6 || ^7 || ^8`
demands a tgz whose package.json#name is "vite" and whose version is
in range. Stop bumping core/CLI to 7.99.0 entirely; pack at 0.0.0 and
post-process a sibling vite-7.99.0.tgz via `tool repack-vite-tgz`,
which now also strips the workspace's vite→vite-plus-core self-ref so
the alias tgz isn't circular.

The test-vp-create matrix picks the override target per package manager
via a new VITE_OVERRIDE_TGZ matrix env. ecosystem-ci/patch-project.ts
mirrors that logic for the bun-vite-template project only; every other
ecosystem project routes vite through the core tgz.
Apply formatting fixes flagged by `vp check`: oxfmt added braces around
single-line if-bodies in build.ts and reflowed the new repack-script
helper and ecosystem-ci patch ternary. No behavior change.
Restores deduping previously done by the bundled wrapper. Without this,
downstream projects co-resolve their own vitest with the one vp-cli ships,
causing undefined `describe` (dual runner state) and TS2769 type clashes.

Pinned to VITEST_VERSION (4.1.5) — the version vp-cli ships transitively.
ecosystem-ci/patch-project.ts mirrors the same overrides so the CI matrix
exercises the same dedup behavior production users get.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User configs like `defineConfig({ test: { globals: true } })` were
failing tsc with TS2769 because vite-plus's defineConfig typed its
parameter as vite-plus-core's UserConfig, which doesn't get vitest's
`declare module 'vite'` augmentation (vite-plus-core is a fork).

Extend the local `declare module '@voidzero-dev/vite-plus-core'`
augmentation to add `test?: VitestInlineConfig`. New unit test
locks the behavior in.
@vitest/mocker's static hoister only recognizes 'vitest' as the
mock-API source string. User configs from migrated projects import
`vi` from 'vite-plus/test', which mocker doesn't see, so vi.mock()
fails at runtime with "problems in resolving the mocks API".

Inject a pre-stage Vite plugin into vite-plus's defineConfig
wrapper that rewrites bare `from 'vite-plus/test'` -> `from 'vitest'`
in user source. Subpaths are left alone. Override pinning (task #50)
guarantees both specifiers resolve to the same module so this is
runtime-safe.

This is a workaround until @vitest/mocker accepts a configurable
hoistedModule option upstream.
…itest/config

`export * from '@voidzero-dev/vite-plus-core'` and `export * from
'vitest/config'` both expose `mergeConfig` (and a handful of other
vite-from-vitest re-exports). Per the ES spec, two star-exports of
the same name make any named import fail with a SyntaxError --
breaking frm-stack and any other config that imports `mergeConfig`
from `vite-plus`.

Switch the vitest/config re-export to an explicit list of names
unique to vitest (configDefaults, coverageConfigDefaults, etc),
leaving the shared names (mergeConfig, loadConfigFromFile, ...) to
come from vite-plus-core only.
A one-off ECONNRESET on the pnpm registry got baked into
command-add-pnpm11-with-workspace/snap.txt, then never reproduced,
making the snap permanently diff against live runs. While fixing,
discovered the existing pnpm WARN redaction regexes contained U+2009
thin-space characters in their patterns, so they never matched real
pnpm output -- only test fixtures that also contained thin spaces.

Normalize all 4 WARN regexes to ASCII space, and broaden the
GET-registry pattern to drop transient network warnings like
`[WARN] GET ... error (ECONNRESET). Will retry ...` so single-run
flakes don't get baked into snapshots.
npmx.dev pins @vitest/coverage-v8@4.1.0 alongside vitest. Without
overriding coverage-v8 to match VITEST_VERSION (4.1.5), pnpm
installs two physical copies of @vitest/mocker on disk and vitest's
runtime mocks-API check (`vi` came from the same mocker copy as
the hoister registered) fails.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vitest/mocker's static hoister hardcodes `hoistedModule = 'vitest'`
and runs as part of vitest's own plugin pipeline that executes
before user plugins -- so the previously-shipped runtime plugin
(d69fe03) cannot intercept it for `vi.mock` calls.

Move the rewrite into the Rust migrator so source is fixed at
migrate-time and the mocker sees the canonical specifier. The
runtime plugin stays as a safety net for code authored after
migration.

Concretely:
- Drop the forward `'vitest'` -> `'vite-plus/test'` ast-grep rule
  (the bare-root specifier is now intentionally preserved).
- Add a reverse `'vite-plus/test'` -> `'vitest'` ast-grep rule that
  is anchored to the exact root specifier; subpaths such as
  `'vite-plus/test/browser-playwright'` are intentionally left
  untouched because they map to dedicated vite-plus shim exports.
- Mirror the same change for `/// <reference types=... />`
  triple-slash directives.
- Update existing tests to expect the new behaviour and add
  dedicated forward + negative coverage for the reverse rewrite.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Trivial fmt-only fix on two test fixtures introduced by 21937c5.
…rite

Commit 21937c5 swapped the Rust migrator's direction from
`'vitest' -> 'vite-plus/test'` (forward) to `'vite-plus/test' -> 'vitest'`
(reverse) to fix vinext's `vi.mock` hoister bug. But that broke
`vp create` because template-generated test files use `from "vitest"`
and the `test-vp-create.yml` CI workflow doesn't include the vitest
family in its `VP_OVERRIDE_PACKAGES` env — so `vitest` is not
installable in the generated project and `vp check` fails with
TS2307.

Restoring the forward rewrite means templates with `from "vitest"`
become `from "vite-plus/test"` (which IS resolvable via the vite-plus
dep). The `vi.mock` hoister bug for migrated projects returns, but
that's already documented as an upstream blocker (PR comment
4466063059) — the cure was worse than the disease.

Coverage-v8/coverage-istanbul overrides from 895e4db are kept.
Task #56 narrowed `export * from 'vitest/config'` to an explicit list,
but missed the vitest-specific type aliases (ViteUserConfig,
ViteUserConfigExport, TestProjectConfiguration, TestUserConfig, etc.).
Downstream like vue-mini imports `ViteUserConfig` from 'vite-plus' and
broke with TS2724.

Add the full set of vitest-only type exports. Runtime exports
(mergeConfig, defineConfig, loadConfigFromFile) still come from
vite-plus-core only to avoid star-export conflicts.
pnpm v10 changed the formatting of the `Packages: +<n>` summary
line (no longer indented). Update the captured snaps for
`command-list-pnpm10-with-workspace` and `command-outdated-pnpm10`
to match. Verified locally that subsequent runs produce no diff.
The mocker rewrite plugin was only injected into the root vite config,
so projects defined under `test.projects` (each spinning up an isolated
Vite pipeline) never got the rewrite — `vi.mock()` failed with
"problems in resolving the mocks API" for source that imports from
`'vite-plus/test'` inside a project.
Fix `vp check` formatting failures from a13027f.
TS infers the narrowed types after `typeof project === 'function'`
and `typeof project === 'object' && project !== null`, so the
explicit casts trip `typescript(no-unnecessary-type-assertion)`.
@Brooooooklyn Brooooooklyn force-pushed the refactor/replace-vite-plus-test-with-vitest branch from 3ffb7cd to a0d248e Compare May 16, 2026 09:20
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0d248e65e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/define-config.ts Outdated
`vp migrate` rewrites `@vitest/browser/context` and
`@vitest/browser-{playwright,preview,webdriverio}/context` to the
corresponding `vite-plus/test/...` subpaths, but those entries
only declared a `types` path. Node's resolver returned
`ERR_PACKAGE_PATH_NOT_EXPORTED` at runtime on migrated browser-mode
projects.

`syncTestPackageExports` now emits a runtime shim that re-exports
from upstream `@vitest/browser/context` (which itself is a stub
that Vitest replaces in browser mode) and adds `default` keys to
all six context entries.
…l success

The initial install (pre-migration) is best-effort — migration
proceeds regardless of its outcome, and the final install (with
--force / --no-frozen-lockfile) is the authoritative recovery.
But `handleInstallResult` was being called on both summaries and
setting `process.exitCode` on either failure, so a successful
final install was being clobbered by a failed initial install's
exit code, making the migration look failed when it wasn't.

Add a `propagateExitCode` opt-out and apply it to the initial
call only. Both failures still get reported via `report.warnings`.
The env var replaces VITE_PLUS_OVERRIDE_PACKAGES entirely, so the
CI's previous map (which only listed vite + vite-plus-core) left
created projects with a catalog missing every vitest entry. Mirror
the vitest family from ecosystem-ci/patch-project.ts so installs
succeed.
- upgrade-deps.ts now bumps the @vitest/browser, @vitest/browser-playwright,
  @vitest/browser-preview, @vitest/browser-webdriverio catalog entries
  alongside the vitest line, and rewrites the VITEST_VERSION constant
  in packages/cli/src/utils/constants.ts.
- ecosystem-ci/patch-project.ts imports VITEST_VERSION from the CLI
  constants so the three places that previously hardcoded "4.1.5"
  collapse to a single source of truth (constants.ts).
Comment thread packages/cli/src/migration/migrator.ts
…l imports

The previous regex-only rewriter ran on raw source text and would
mutate string content that happened to contain `from 'vite-plus/test'`
(template literals, error messages, fixtures). Switch the ESM
import/dynamic-import path to es-module-lexer so only actual import
specifiers get spliced. CJS `require()` keeps a tightened
boundary-anchored regex.

Added 4 unit tests covering the false-positive cases.
`LEGACY_WRAPPER_FALLBACK_VERSIONS.vitest` was set to `^${VITEST_VERSION}`,
while fresh migrations write the exact `VITEST_VERSION` via
`VITE_PLUS_OVERRIDE_PACKAGES`. Projects cleaned up from a stale
`@voidzero-dev/vite-plus-test` alias should land on the same exact
pin as fresh migrations, not a caret range that allows unintended
drift on reinstall.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a5b2697e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


export function defineConfig(config: ViteUserConfigExport): ViteUserConfigExport {
return viteDefineConfig(config);
return viteDefineConfig(injectPluginIntoConfig(config));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Inject vitest-specifier rewrite without requiring defineConfig

The hoisting workaround only runs when users call defineConfig from vite-plus, because plugin injection is hard-wired to that wrapper; Vitest configs that export a plain object/function (or no config file at all) never receive the rewrite plugin. In those common setups, test files that import vi from vite-plus/test still reach Vitest with the original specifier, so vi.mock(...) is not hoisted and mocks can execute too late. Fresh evidence versus the earlier thread: this commit wires the rewrite exclusively in defineConfig(...) and there is no CLI-side fallback path that applies it universally.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1a5b269. Configure here.

- name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }}
working-directory: ${{ runner.temp }}
env:
VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/${{ env.VITE_OVERRIDE_TGZ }}","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"4.1.5","@vitest/expect":"4.1.5","@vitest/runner":"4.1.5","@vitest/snapshot":"4.1.5","@vitest/spy":"4.1.5","@vitest/utils":"4.1.5","@vitest/mocker":"4.1.5","@vitest/pretty-format":"4.1.5","@vitest/coverage-v8":"4.1.5","@vitest/coverage-istanbul":"4.1.5"}'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded vitest version in CI workflow diverges on bump

Medium Severity

VP_OVERRIDE_PACKAGES in test-vp-create.yml hardcodes "4.1.5" ten times for vitest and @vitest/* packages. The upgrade-deps.ts script updates VITEST_VERSION in constants.ts and the pnpm-workspace.yaml catalog, but does not touch this workflow file. After the first daily vitest bump, the CI vp create tests will pin vitest to a stale version while the rest of the repo expects the newer one, silently testing with mismatched dependencies. The old code used file: tgz references for vitest that didn't require version-string synchronization, so this is a new maintenance gap introduced by this PR.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1a5b269. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant